#include "StdAfx.h"

#include <QVector3D>

#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2dAPI_InterCurveCurve.hxx>
#include <Geom2dAPI_ProjectPointOnCurve.hxx>

#include "Extend2DHandler.h"
#include "SelectHandler3D.h"
#include "Qt3DPicker.h"
#include "CmdManager.h"
#include "QtOSGWidget.h"
#include "OsgUtil.h"
#include "Scene.h"
#include "Operation/OpReplace.h"
#include "OcctShapeProp.h"

std::vector<gp_Pnt2d> intersectForExtend(Handle(Geom2d_Curve) curve, bool front, const std::vector<Handle(Geom2d_Curve)>& boundaries)
{
	std::vector<gp_Pnt2d> result;
	auto trimmedCurve = Handle(Geom2d_TrimmedCurve)::DownCast(curve.get());
	if (!trimmedCurve)
		return result;
	if (!trimmedCurve->IsClosed())
	{
		if (front)
		{
			double lp = trimmedCurve->LastParameter();
			if (basisCurve(trimmedCurve)->IsPeriodic())
			{
				double period = basisCurve(trimmedCurve)->Period();
				curve = new Geom2d_TrimmedCurve(trimmedCurve->BasisCurve(), lp - period, lp);
			}
			else
			{
				auto basicCurve = basisCurve(trimmedCurve);
				curve = new Geom2d_TrimmedCurve(trimmedCurve->BasisCurve(), basicCurve->FirstParameter(), lp);
			}
		}
		else
		{
			double fp = trimmedCurve->FirstParameter();
			if (basisCurve(trimmedCurve)->IsPeriodic())
			{
				double period = basisCurve(trimmedCurve)->Period();
				curve = new Geom2d_TrimmedCurve(trimmedCurve->BasisCurve(), fp, fp + period);
			}
			else
			{
				auto basicCurve = basisCurve(trimmedCurve);
				curve = new Geom2d_TrimmedCurve(trimmedCurve->BasisCurve(), fp, basicCurve->LastParameter());
			}
		}
	}

	Geom2dAPI_InterCurveCurve inter;
	for (const auto& bound : boundaries)
	{
		inter.Init(curve, bound);
		for (int i = 1; i <= inter.NbPoints(); ++i)
		{
			result.push_back(inter.Point(i));
		}
	}
	return result;
}

Handle(Geom2d_TrimmedCurve) extend(Handle(Geom2d_Curve) curve, bool front, double t)
{
	auto trimmedCurve = Handle(Geom2d_TrimmedCurve)::DownCast(curve.get());
	if (!trimmedCurve)
		return nullptr;
	Handle(Geom2d_TrimmedCurve) extendedCurve;
	double fromParam, toParam;
	if (front)
	{
		fromParam = t;
		toParam = trimmedCurve->LastParameter();
	}
	else
	{
		fromParam = trimmedCurve->FirstParameter();
		toParam = t;
	}
	//auto baseCurve = basisCurve(curve);
	//if (baseCurve->IsPeriodic())
	//{
	//	double period = baseCurve->Period();
	//	if (std::abs(toParam - fromParam - period) < Precision::Confusion())
	//	{
	//		fromParam = baseCurve->FirstParameter();
	//		toParam = baseCurve->LastParameter();
	//	}
	//}
	extendedCurve = new Geom2d_TrimmedCurve(trimmedCurve->BasisCurve(), fromParam, toParam);
	return extendedCurve;
}

Extend2DHandler::Extend2DHandler()
{

}

void Extend2DHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	//Esc is pressed
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			selector->clearSelection();
			clearInput();
			emit finished();
			return;
		}
	}

	if(status() == 0)
	{
		if (selector)
		{
			selector->clearSelection();
			selector->beginSelect(D2CurveMask);
			prompt(1, tr("选择二维边界曲线: "));
		}
		else
		{
			emit finished();
		}
		return;
	}

	if(status() == 1)
	{
		if (selector && selector->done())
		{
			auto selections = selector->selectionSet();
			if (selections.size() > 0)
			{
				std::vector<std::shared_ptr<Ent>> origins;
				std::vector<std::shared_ptr<Ent>> explodes;
				for (auto selection : selections)
				{
					auto ent = std::dynamic_pointer_cast<Ent2DCurve>(selection);
					if (ent)
					{
						auto parent = ent->getParent();
						UCS* ucs = dynamic_cast<UCS*>(parent);
						if (ucs)
						{
							if (m_ucs == nullptr)
								m_ucs = ucs;
							if (ucs == m_ucs)
							{
								m_EXBorders.insert(ent);
							}
							else
							{
								remark(tr("请选择同一平面的二维图形，已忽略"));
							}
						}
						else
						{
							remark(tr("请选择二维图形"));
							emit finished();
							return;
						}
					}
				}
				selector->clearSelection();
				if (m_EXBorders.empty())
				{
					emit finished();
					return;
				}
				else
				{
					selector->beginSelect(D2CurveMask, 1);
					prompt(2, tr("选择二维待延伸曲线: "));
				}
			}
		}
		return;
	}

	if (status() == 2)
	{
		if (selector->done())
		{
			std::set<std::shared_ptr<INode>> nodes = selector->selectionSet();
			selector->clearSelection();
			if (nodes.empty())
			{
				emit finished();
				return;
			}
			std::shared_ptr<INode> node = *nodes.begin();
			auto curve = std::dynamic_pointer_cast<Ent2DCurve>(node);
			if (curve)
			{
				auto parent = curve->getParent();
				UCS* ucs = dynamic_cast<UCS*>(parent);
				if (ucs)
				{
					if (ucs == m_ucs)
					{
						if (m_EXBorders.find(curve) != m_EXBorders.end())
						{
							remark(tr("对象属于边界，不能延伸"));
							emit finished();
							return;
						}
						std::vector<Handle(Geom2d_Curve)> curve2ds = curve->getCurve2d();
						if (curve2ds.empty())
						{
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
						std::vector<Handle(Geom2d_Curve)> boundaries;
						for (const auto& border : m_EXBorders)
						{
							auto tmp = border->getCurve2d();
							boundaries.insert(boundaries.end(), tmp.begin(), tmp.end());
						}
						auto widget3D = getWidget();
						osg::Vec2d cursor = widget3D->getCursorPoint();
						osg::Camera* camera = widget3D->getCamera();
						if (!camera)
						{
							emit finished();
							return;
						}
						osg::Matrix VPW, invVPW;
						bool success = getCameraViewProjectionWindowMatrix(camera, VPW, invVPW);
						if (!success)
						{
							emit finished();
							return;
						}
						osg::Vec3d worldPt(cursor.x(), cursor.y(), 0);
						worldPt = worldPt * invVPW;
						osg::Vec3d ucsPt;
						getViewPointOnPlane(*m_ucs, worldPt, ucsPt);
						gp_Pnt2d ucsPnt(ucsPt.x(), ucsPt.y());
						Handle(Geom2d_Curve) toBeExtendedCurve;
						bool front;
						double oldParam;
						if (curve2ds.size() == 1)
						{
							double fp = curve2ds[0]->FirstParameter();
							auto fPnt = curve2ds[0]->Value(fp);
							double lp = curve2ds[0]->LastParameter();
							auto lPnt = curve2ds[0]->Value(lp);
							toBeExtendedCurve = curve2ds[0];
							if (ucsPnt.SquareDistance(fPnt) < ucsPnt.SquareDistance(lPnt))
							{
								front = true;
								oldParam = fp;
							}
							else
							{
								front = false;
								oldParam = lp;
							}
						}
						else
						{
							double fp = curve2ds[0]->FirstParameter();
							auto fPnt = curve2ds[0]->Value(fp);
							int lastIndex = curve2ds.size() - 1;
							double lp = curve2ds[lastIndex]->LastParameter();
							auto lPnt = curve2ds[lastIndex]->Value(lp);
							if (ucsPnt.SquareDistance(fPnt) < ucsPnt.SquareDistance(lPnt))
							{
								toBeExtendedCurve = curve2ds[0];
								front = true;
								oldParam = fp;
							}
							else
							{
								toBeExtendedCurve = curve2ds[lastIndex];
								front = false;
								oldParam = lp;
							}
						}
						bool hasParam = false;
						auto basicCurve = basisCurve(toBeExtendedCurve);
						double newParam;
						if (basicCurve->IsPeriodic())
						{
							double period = basicCurve->Period();
							newParam = front ? toBeExtendedCurve->LastParameter() - period : toBeExtendedCurve->FirstParameter() + period;
						}
						else
						{
							newParam  = front ? basicCurve->FirstParameter() : basicCurve->LastParameter();
						}
						
						auto points = intersectForExtend(toBeExtendedCurve, front, boundaries);
						std::vector<double> xParams;
						const double PARAM_ERROR = 1E-4;
						for (const auto& pt : points)
						{
							double xParam;
							bool success = getParam(toBeExtendedCurve, true, pt, xParam);
							if (success)
							{
								if (front)
								{
									if (basicCurve->IsPeriodic())
									{
										double period = basicCurve->Period();
										while (xParam - oldParam > PARAM_ERROR)
										{
											xParam -= period;
										}
										while (PARAM_ERROR < oldParam - period - xParam)
										{
											xParam += period;
										}
									}
									if (oldParam - xParam > PARAM_ERROR && (xParam - newParam > PARAM_ERROR))
									{
										xParams.push_back(xParam);
									}
								}
								else
								{
									if (basicCurve->IsPeriodic())
									{
										double period = basicCurve->Period();
										while (PARAM_ERROR < oldParam - xParam)
										{
											xParam += period;
										}
										while (xParam -(oldParam + period)> PARAM_ERROR)
										{
											xParam -= period;
										}
									}
									if (xParam - oldParam > PARAM_ERROR && (newParam-xParam > PARAM_ERROR))
									{
										xParams.push_back(xParam);
									}
								}
							}
						}
						if (xParams.empty())
						{
							remark(tr("无法延伸此对象"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
						if (front)
						{
							newParam = -DBL_MAX;
							for (auto xParam : xParams)
							{
								if (xParam > newParam)
								{
									newParam = xParam;
								}
							}
						}
						else
						{
							newParam = DBL_MAX;
							for (auto xParam : xParams)
							{
								if (xParam < newParam)
								{
									newParam = xParam;
								}
							}
						}
						Handle(Geom2d_TrimmedCurve) extendedCurve = extend(toBeExtendedCurve, front, newParam);
						if (!extendedCurve)
						{
							remark(tr("无法延伸此对象"));
							selector->beginSelect(D2CurveMask, 1);
							return;
						}
						double toParam;
						gp_Pnt2d toPoint;
						if (front)
						{
							toParam = extendedCurve->FirstParameter();
							toPoint = extendedCurve->StartPoint();
						}
						else
						{
							toParam = extendedCurve->LastParameter();
							toPoint = extendedCurve->EndPoint();
						}
						auto newCurve = curve->extend(front, toParam, toPoint);
						std::vector<std::shared_ptr<Ent>> newEnts;
						std::vector<std::shared_ptr<Ent>> oldEnts;
						newEnts.push_back(newCurve);
						oldEnts.push_back(curve);
						auto scene = getScene();
						auto undoStack = scene->undoStack();
						undoStack->push(new OpReplace(oldEnts, newEnts));

						selector->beginSelect(D2CurveMask, 1);
					}
					else
					{
						remark(tr("请选择同一平面的二维图形，已忽略"));
						selector->beginSelect(D2CurveMask, 1);
					}
				}
				else
				{
					remark(tr("请选择二维图形，已忽略"));
					selector->beginSelect(D2CurveMask, 1);
					return;
				}
			}

		}
	}
}
